package GUI;
import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferStrategy;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.awt.image.ImageProducer;
import java.awt.image.RGBImageFilter;
import java.util.ArrayList;
import java.util.List;
import Exceptions.MoveException;
import Exceptions.StrategoException;
import Logic.IPlayEngine;
import Logic.MoveResults;
import Logic.Square;
import Logic.Square.Rank;
import Logic.Square.Type;
import Logic.Objects.PlayEngine;
import Logic.Objects.Player;

public class GUI implements KeyListener,MouseListener {
    Image screenImage;
	static String fBgMusic = System.getProperty("user.dir")+"/src/Resources/bg.mp3";
	static String fClickSound= System.getProperty("user.dir")+"/src/Resources/Armymarch.mp3";
	private boolean exitGame = false;
	
    private static DisplayMode[] BEST_DISPLAY_MODES = new DisplayMode[] {
        new DisplayMode(640, 480, 32, 0),
        new DisplayMode(640, 480, 16, 0),
        new DisplayMode(640, 480, 8, 0)
    };
    
    Frame mainFrame;
    BufferStrategy bufferStrategy;
    int w,h,x;
    Image logoImage;
    Image boardImage;
    Image pieces;
    Image arrows;
    Image pImage;
    Image torch;
    Image skull;
    Image sword;
    Image waterTile;
    Image[] AIIcons = new Image[4];
    Image[] HumanIcons = new Image[4];
    Player[] _localPlayerList = null;
    IPlayEngine engine;
    boolean isGameOver_ = false;
    List<TimeoutObject> timeoutObjects = new ArrayList<TimeoutObject>();
    int playerId= 1;
    boolean waitForKey = false;
    Graphics g;
    
    
    static final int PIECE_WIDTH = 91;
    static final int PIECE_HEIGHT = 91;
    static final int BOARD_WIDTH = 18;
    static final int BOARD_HEIGHT = 18;
    static final int ARROW_WIDTH = 78;
    static final int ARROW_HEIGHT = 67;
    private double grid_width = 0;
    private double grid_height = 0;
    
    // offset for the board, depending on the ratio
    private int offsX = 0;
   
  
    private MouseState mouseState  = new MouseState();
    /*
     * Some gfx related functions
     */
   
    private void clearBuffer()
    {
    	g.setColor(Color.black);
        g.fillRect(0,0,w, h);
    }
    private void flip()
    {
    	  bufferStrategy.show();
    }
   
    private void drawPiece(int x,int y,Square square,boolean selected)
    {
    
    	if (square.getType()==Type.Water)
    	{
    		g.drawImage(waterTile,(int)Math.round((x-1)*grid_width)+offsX+2,
        			(int)Math.round((y-1)*grid_height)+3,(int)Math.round(x*grid_width)+offsX-2,
        			(int)Math.round(y*grid_height-3)
        				,0,0,waterTile.getWidth(null),waterTile.getHeight(null),null);
    		return;
    	}
    	if (square.getType() != Type.Soldier)
    		return;
    	
    	int x1 = 0;
    	switch(square.getRank())
    	{
    	case Marshal:
    		x1 = 0;
    		break;
    	case General:
    		x1 = PIECE_WIDTH;
    		break;
    	case Colonel:
    		x1 = PIECE_WIDTH*2;
    		break;
    	case Major:
    		x1 = PIECE_WIDTH*3;
    		break;
    	case Captain:
    		x1 = PIECE_WIDTH*4;
    		break;
    	case Lieutenant:
    		x1 = PIECE_WIDTH*5;
    		break;
    	case Sergeant:
    		x1 = PIECE_WIDTH*6;
    		break;
    	case Miner:
    		x1 = 7*PIECE_WIDTH;
    		break;
    	case Scout:
    		x1 = 8*PIECE_WIDTH;
    		break;
    	case Bomb:
    		x1 = 9*PIECE_WIDTH;
    		break;
    	case Flag:
    		x1 = 10*PIECE_WIDTH;
    		break;
    	case Spy:
    		x1 = 11*PIECE_WIDTH;
    		break;	
    		
    	case Unknown:
    		x1 = 12*PIECE_WIDTH;
    	}
    	
    	int id = square.getPlayerId();
    	int y1 = (id-1) * PIECE_HEIGHT;
    	if (selected)
    		y1 = (id % 4) * PIECE_HEIGHT;
    	
    		g.drawImage(pieces,(int)Math.round((x-1)*grid_width)+offsX-5,
    			(int)Math.round((y-1)*grid_height),(int)Math.round(x*grid_width)+offsX+5,
    			(int)Math.round(y*grid_height)
    				,x1,y1,x1+PIECE_WIDTH,y1+PIECE_HEIGHT,null);
 
    }
    private void drawPieces() throws StrategoException
    {
    	Square square;
    	Square[][] squares = getBoard();
    	
    	for (int y=0;y< BOARD_HEIGHT;y++) {
    		for (int x=0;x<BOARD_WIDTH;x++)
    		{
    			square = squares[x][y];
    			if  (square != null) 
    			{
    				
    				drawPiece(x+1, y+1, square,false);
    				
    			}
    		
    		}
    	}
    	
    	if (mouseState.state.equals(mouseState_.ChooseWhereTo)) {
    		Square temp =squares[mouseState.chosedX-1][mouseState.chosedY-1];
    		if (temp.getPlayerId() != playerId)
    			return;
    		drawPiece(mouseState.chosedX, mouseState.chosedY, temp,true);
    	}
    }

    private Square[][] board = null;
    private int lastPlayer = -1;
	private List<MoveResults> lastMoves = null;
    
	private Square[][] getBoard() throws StrategoException {
		updateBoardAndLastMoves();
		return board;
	}
	
	private List<MoveResults> getLastMoves() throws StrategoException {
		updateBoardAndLastMoves();
		return lastMoves;
	}
	
	private void updateBoardAndLastMoves() throws StrategoException {
		if (board == null || lastMoves == null || isCurrentPlayerUpdated() || lastPlayer != getCurrentPlayer()) {
			Object[] res = engine.getCurrentPlayer_And_Display_And_LastMovesForPlayer(
					getLocalPlayerList().length,playerId); 
			board = (Square[][]) res[0];
			lastMoves  = (List<MoveResults>) res[1];
			setCurrentPlayer((Integer) res[2]);
			lastPlayer = (Integer) res[2];
		}
	}
	
	private long lastUpdate = -1;
	private int currentPlayer = -1;
	private int getCurrentPlayer() throws StrategoException {
		if (currentPlayer == playerId) return currentPlayer;
		
		if (isCurrentPlayerUpdated()) {
			setCurrentPlayer(engine.getCurrentPlayer(playerId));
		}
		return currentPlayer;
	}

	private void setCurrentPlayer(int currentPlayer) {
		lastUpdate = System.currentTimeMillis();
		this.currentPlayer = currentPlayer;
	}
	
	private boolean isCurrentPlayerUpdated() {
		return lastUpdate == -1 || System.currentTimeMillis() - lastUpdate > 2000;
	}
	private long lastGameOverUpdate = 0;
	private boolean isGameOver() throws StrategoException {
		
		if (lastGameOverUpdate == -1 || System.currentTimeMillis() - lastGameOverUpdate > 4000) {
			lastGameOverUpdate = System.currentTimeMillis();
			isGameOver_ = engine.isGameOver();
		}
		return isGameOver_;
	}
    private void drawIcons()
    {
    	int offset = 0;
    	Player[] localPlayerList = getLocalPlayerList();
    	
    	for (int i=0;i<localPlayerList.length;i++)
    	{
    		if (localPlayerList[i]==null)
    			continue;
    		if (localPlayerList[i].isHuman())
    		{
    			g.drawImage(HumanIcons[i],0, 0+offset, 50, 50+offset, 0, 0, HumanIcons[i].getWidth(null)
    	    			, HumanIcons[i].getHeight(null), null);
    	    	g.setFont(new Font("Times New Roman", Font.BOLD, 20));
    	    	g.setColor(Color.BLUE);
    	    	g.drawString(localPlayerList[i].getName(), 60, 30+offset);
    	    	
    		}
    		else
    		{
    			g.drawImage(AIIcons[i],0, 0+offset, 50, 50+offset, 0, 0, AIIcons[i].getWidth(null)
    	    			, AIIcons[i].getHeight(null), null);
    	    	g.setFont(new Font("Times New Roman", Font.BOLD, 20));
    	    	g.setColor(Color.BLUE);
    	    	g.drawString(localPlayerList[i].getName(), 60, 30+offset);
    	    	
    	    	
    		}
    		
    		
    		switch (localPlayerList[i].getLastEvent())
	    	{
	    	case move:
	    		g.drawImage(torch, 150, 0+offset, 200, 50+offset, 0, 0, torch.getWidth(null)
	    				, torch.getHeight(null), null);
	    		break;
	    	case lose:
	    		
	    		g.drawImage(skull, 150, 0+offset, 200, 50+offset, 0, 0, skull.getWidth(null)
	    				, skull.getHeight(null), null);
	    		break;
	    	case win:
	    		g.drawImage(sword, 150, 0+offset, 200, 50+offset, 0, 0, sword.getWidth(null)
	    				, sword.getHeight(null), null);
	    		break;
	    	
	    	};
    		
    		offset += 100;
    		
    		if (localPlayerList[i].getID() == currentPlayer)
    		{
    			g.setColor(Color.white);
        		
        		g.draw3DRect(0, offset-100, (int) (w*0.19), 50, true);
    		}
    			
    	}
    	if (currentPlayer > 0)
    	{
    		
    		g.setColor(Color.YELLOW);
    		g.drawString("Current Player ", 15, h-200);
    		g.setColor(Color.WHITE);
    		g.drawString(localPlayerList[currentPlayer-1].getName(), 25, h-100);
    	}
    }
    
    private void drawTimeoutObjects()
    {
    	ArrayList<TimeoutObject> temp = new ArrayList<TimeoutObject>();
    	
    	if (timeoutObjects.size()==0)
    		return;
   
    	for (TimeoutObject obj : timeoutObjects)
    	{
    		if (obj.hold>0)
    			obj.hold--;
    		else
    			if (obj.timeout>0)
    			{
    				obj.timeout--;
    				obj.action();
    			}
    		
    		
    	}
    	if (timeoutObjects.size() >= 5)
    	{
    		for (TimeoutObject obj: timeoutObjects)
    			if (obj.timeout>0)
    				temp.add(obj);
    		
    		timeoutObjects = temp;
    	}	
    }
    
    private void drawLastMoves() throws StrategoException
    {
    	List<MoveResults> lastMoves = getLastMoves();
    	if (lastMoves.size() == 0) return;
    	for (MoveResults move : lastMoves)
    	{
	    	Square s1 = move.getSoldier1();
	    	int x1 = move.getX1();
	    	int y1 = move.getY1();
	    	
	    	
	    	Square s2 = move.getSoldier2();
	    	int x2 = move.getX2();
	    	int y2 = move.getY2();
	    	
	    	switch (move.getType())
	    	{
	    	case move:
	    		int ax = (s1.getPlayerId()-1) * ARROW_WIDTH;
	    		int ay = 0;
	    		if (x1<x2)
	    			ay = 1 * ARROW_HEIGHT;
	    		else
	    			if (x1>x2)
	    				ay = 2 * ARROW_HEIGHT;
	    			else
	    				if (y1<y2)
	    					ay = 0 * ARROW_HEIGHT;
	    				else
	    					ay = 3 * ARROW_HEIGHT;
	    			
		    	g.drawImage(arrows,(int)Math.round((x1-1)*grid_width)+offsX-5,
		    			(int)Math.round((y1-1)*grid_height),(int)Math.round(x1*grid_width)+offsX,
		    			(int)Math.round(y1*grid_height)
		    				,ax,ay,ax+ARROW_WIDTH,ay+ARROW_HEIGHT,null);
		    	break;
	    		
	    	default:
	    		if ( (s1.getPlayerId() != playerId) && (s2.getPlayerId() != playerId) )
	    			return;
	    		int key1 = x1*17+y1*32+s1.hashCode()*12;
	    		int key2 = x2*23+y2*12+s2.hashCode()*15;

	    		boolean skip1 = false;
	    		boolean skip2 = false;
	
	    		
	    		
	    		
	    		for (TimeoutObject obj : timeoutObjects)
	    		{
	    			if (obj.key == key1) 
	    				skip1 = true;
	    			if (obj.key == key2)
	    				skip2 = true;
	    			
	    		}
	    		
	    		if (!skip1)
	    			 timeoutObjects.add(new TimeoutSquare(x1,y1,s1,20,key1));
	    		if (!skip2)
	    			 timeoutObjects.add(new TimeoutSquare(x2,y2,s2,20,key2));   		
	    		
	    	}
	    	
    	}
    }
    // main drawing function
    private void drawAllToBuffer() throws StrategoException
    {
    	//draw board
    	g.drawImage(boardImage,0,0,w,h-(int)Math.abs(h-(grid_height*18)),0,0,boardImage.getWidth(null),boardImage.getHeight(null)
    				,null);
    	
    	drawIcons();
 
    	drawPieces();
    	
    	drawLastMoves();
    	
    	drawTimeoutObjects();
    	
    }
    private void loadResources() throws InterruptedException
    {
    	boardImage =  Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/background.jpg"));
    	pImage     =  Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/pieces.png"));
    	waterTile =   Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/water.png"));
    	AIIcons[0] =  Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/computer-blue.png"));
    	AIIcons[1] =  Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/computer-green.png"));
    	AIIcons[2] =  Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/computer-orange.png"));
    	AIIcons[3] =  Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/computer-red.png"));
    	
    	HumanIcons[0] = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/player-blue.png"));
    	HumanIcons[1] = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/player-green.png"));
    	HumanIcons[2] = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/player-orange.png"));
    	HumanIcons[3] = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/player-red.png"));
    	
    	arrows        = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/arrows.jpg"));
    	torch         = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/torch.gif"));
    	skull         = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/skull.gif"));
    	sword         = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/art/sword.gif"));
    	
    	MediaTracker t = new MediaTracker(mainFrame);
        t.addImage(logoImage, 1);
        t.addImage(boardImage, 2);
        t.addImage(pImage, 3);
        
        t.addImage(waterTile, 4);
        
        for (int i=0;i<4;i++) 
        	t.addImage(HumanIcons[i], 5+i);
        
        for (int i=0;i<4;i++) 
        	t.addImage(AIIcons[i], 9+i);
        
        t.addImage(arrows, 13);
        t.addImage(skull, 14);
        t.addImage(torch,15);
        t.addImage(sword, 15);
        
        t.waitForAll();
       
        
    	ImageFilter filter = new RGBImageFilter() {
    	      // the color we are looking for... Alpha bits are set to opaque
    	      public int markerRGB = Color.black.getRGB() | 0xFF000000;

    	      public final int filterRGB(int x, int y, int rgb) {
    	        if ( ( rgb | 0xFF000000 ) == markerRGB ) {
    	          // Mark the alpha bits as zero - transparent
    	          return 0x00FFFFFF & rgb;
    	          }
    	        else {
    	          // nothing to do
    	          return rgb;
    	          }
    	        }
    	      }; 
    	
      
    	ImageProducer ip = new FilteredImageSource(pImage.getSource(), filter);
    	ImageProducer ip2 = new FilteredImageSource(waterTile.getSource(),filter);
    	
    	pieces = Toolkit.getDefaultToolkit().createImage(ip);
    	waterTile = Toolkit.getDefaultToolkit().createImage(ip2);
    	arrows = Toolkit.getDefaultToolkit().
    	createImage(new FilteredImageSource(arrows.getSource(), filter));
    	
    	t.addImage(pieces, 18);
    	t.addImage(waterTile, 19);
    	t.addImage(arrows, 20);
    	t.waitForAll();
    }
    /*
     * end of gfx functions
     */
    
    /**
     * COTR - handles all the starting configuration
     */
    public GUI(int numBuffers, GraphicsDevice device,IPlayEngine _engine, String userName) {
        try {
        	Player player;
        	boolean AIHandler = false;
        	
            this.engine = _engine;
            this.playerId = engine.getPlayerID(userName);
       
            for (int i=1;i<=4;i++)
            {
              
         	  player = engine.getPlayer(i);
         	 
         	  if (( player != null) && (player.isHuman()))
         	  {
         		  AIHandler = true;
         		  break;
         	  }
         	  	
         	  
            }
            
            GraphicsConfiguration gc = device.getDefaultConfiguration();
            mainFrame = new Frame(gc);
            mainFrame.setUndecorated(true);
            mainFrame.setIgnoreRepaint(true);
            mainFrame.addKeyListener(this);
            mainFrame.addMouseListener(this);
        
            device.setFullScreenWindow(mainFrame);
           
            Rectangle bounds = mainFrame.getBounds();
            mainFrame.createBufferStrategy(numBuffers);
            bufferStrategy = mainFrame.getBufferStrategy();
        
           g = bufferStrategy.getDrawGraphics();
           w = bounds.width;
           h = bounds.height;
           
           offsX = (int)Math.round(w * 0.19);
           grid_width = (w * 0.81) / 18;
           grid_height = (h / 18);
           
           loadResources();
          
           // main dispatch loop
           
           
          
           while (true)
           {
        	  
        	 
        	   /* draw frame 1 times before updating from server */
        	   for (int i=0;i<10;i++)
        	   {
        		   clearBuffer();
        		   drawAllToBuffer();
        		   flip();
        		   Thread.sleep(20);
        	   }
        	  
        	   getCurrentPlayer();
     
        	   if (exitGame) {
        		   engine.leaveGame(playerId);
        		   System.exit(0);
        		   break;
        	   }
        	   if (isGameOver()) {
    			   break;
        	   }
 
        	   try {
        		   if (AIHandler && (getCurrentPlayer() > 0))
        		   {
        				   
        			   player = getLocalPlayerList()[getCurrentPlayer()-1];
        			   
        			   if (player!=null && !player.isHuman() && (player.isAlive()) ) {
        				   MoveResults result = engine.playAI();
        				   currentPlayer = -1;
        			   }
        	   		}
        	   }
        	   catch (StrategoException e)
        	   {
        		   //didnt finish to update, try again
        	   }
           
        	   
           }	           	   
   
           
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            device.setFullScreenWindow(null);
            mainFrame.dispose();
//            System.exit(0);
        }

    }
    
    long lastPlayersUpdate = 0;
    
    private Player[] getLocalPlayerList() {
    	if (_localPlayerList == null || System.currentTimeMillis() - lastPlayersUpdate > 20000) {
    		_localPlayerList = engine.getAllPlayers();
    		lastPlayersUpdate = System.currentTimeMillis();
    	}
    	return _localPlayerList;
	}

	private static DisplayMode getBestDisplayMode(GraphicsDevice device) {
        for (int x = 0; x < BEST_DISPLAY_MODES.length; x++) {
            DisplayMode[] modes = device.getDisplayModes();
            for (int i = 0; i < modes.length; i++) {
                if (modes[i].getWidth() == BEST_DISPLAY_MODES[x].getWidth()
                   && modes[i].getHeight() == BEST_DISPLAY_MODES[x].getHeight()
                   && modes[i].getBitDepth() == BEST_DISPLAY_MODES[x].getBitDepth()
                   ) {
                    return BEST_DISPLAY_MODES[x];
                }
            }
        }
        return null;
    }
    
    public static void chooseBestDisplayMode(GraphicsDevice device) {
        DisplayMode best = getBestDisplayMode(device);
        if (best != null) {
            device.setDisplayMode(best);
        }
    }
    
    public static void start(IPlayEngine engine, String userName)
    {
    	   try {
    		    MP3 clickSound = new MP3(fBgMusic);
    		    clickSound.play();
               int numBuffers = 2;
             
               GraphicsEnvironment env = GraphicsEnvironment.
                   getLocalGraphicsEnvironment();
               GraphicsDevice device = env.getDefaultScreenDevice();
               GUI test = new GUI(numBuffers, device,engine,userName);
           } catch (Exception e) {
               e.printStackTrace();
           }
 
    }
    
    public static void main(String[] args) {
    	PlayEngine engine = new PlayEngine();
    	 engine.setBoard(BOARD_WIDTH, BOARD_HEIGHT);
         engine.setNumberOfPlayers(4);
         engine.setAIPlayer(1);
         engine.setAIPlayer(3);
         engine.setAIPlayer(4);
         try {
			 engine.SetMySoldiersRandomlly(1);
			 engine.SetMySoldiersRandomlly(2);
	         engine.SetMySoldiersRandomlly(3);
	         engine.SetMySoldiersRandomlly(4);
		} catch (StrategoException e) {
			e.printStackTrace();
		}
        
		 engine.getPlayer(1).setName("itay");
		 engine.getPlayer(2).setName("lior");
		 engine.getPlayer(3).setName("baba");
		 engine.getPlayer(4).setName("papa");
         engine.setCurrentPlayer(2);
         
         start(engine, "lior");
    }

	@Override
	public void keyPressed(KeyEvent arg0) {
		waitForKey = false;
		if (arg0.getKeyCode() == KeyEvent.VK_ESCAPE) {
			exitGame = true;			
		}
		
	}

	@Override
	public void keyReleased(KeyEvent arg0) {
		
	}

	@Override
	public void keyTyped(KeyEvent arg0) {
		
	}

	@Override
	public void mouseClicked(MouseEvent e) {
		
	}

	@Override
	public void mouseEntered(MouseEvent e) {
		
	}

	@Override
	public void mouseExited(MouseEvent e) {
		
	}

	@Override
	public void mousePressed(MouseEvent e) {
		try {
			if (playerId != getCurrentPlayer())
				return;
		} catch (StrategoException e2) {
			e2.printStackTrace();
		}
		
		int x = e.getX();
		int y = e.getY();
		int blockX = (int)(((float)x-w*0.19) / grid_width +1);
		
		// not in the board range
		if (blockX<1)
			return;
		int blockY = (int)(y / grid_height + 1);
		
		if (mouseState.state == mouseState_.ChooseSoldierClick)
		{
			try {
				chooseSoldierToMove(blockX, blockY);
			} catch (StrategoException e1) {
				e1.printStackTrace();
			}
		}
		else
		{
			try {
				if ( (blockX == mouseState.chosedX) &&  (blockY == mouseState.chosedY)){
					cleanChooseSoldierToMove();
					return;
				}
				chooseWhereTo(blockX,blockY);
			    MP3 clickSound = new MP3(fClickSound);
			    clickSound.play();
			} catch (StrategoException e1) {
				e1.printStackTrace();
			}
		}

	
	}

	public void mouseReleased(MouseEvent e) {
		
	}
	
	private void cleanChooseSoldierToMove() throws StrategoException{
		clearBuffer();
		drawAllToBuffer();
		flip();
		mouseState.state = mouseState_.ChooseSoldierClick;
	}
		
	private void chooseSoldierToMove(int blockX,int blockY) throws StrategoException
	{
		boolean canMove = false;
		if (currentPlayer != playerId)
			return;
		Square[][] board = getBoard();
		Square square = board[blockX-1][blockY-1]; //engine.getBoardXY(blockX, blockY);
		if (square == null)
			return;
		
		if (square.getType() != Type.Soldier){
			//TODO: show error message
			return;
		}
		
		else if(square.getPlayerId() != playerId) {
			//TODO: show error message
			return;
		}
		
		// check we have a place to move
		for (int i=-1;i<=1;i++){
			for (int j=-1;j<=1;j++)
			{
				if  ( ( Math.abs(i) + Math.abs(j) ) != 1) continue;
				// calculate sides

				Square nextSquare = null;
				if ((blockX+j-1>=0) && (blockX+j-1<18)
					&& (blockY+i-1>=0) && (blockY+i-1<18) )
						nextSquare = board[blockX+j-1][blockY+i-1];
				
				if (nextSquare == null) {
					canMove = true;
					break;
				}

				if (( ( nextSquare.getType()==Square.Type.Soldier)  && (nextSquare.getPlayerId() == playerId) )
					|| (!nextSquare.isWalkable()))  continue; //cant move on your own
				
				canMove = true;
				break;
				
			}
		}
		
		if (!canMove)
			return;
		
		mouseState.chosedX = blockX;
		mouseState.chosedY = blockY;
		mouseState.chosedRank = square.getRank();
		mouseState.state = mouseState_.ChooseWhereTo;
	}
		
	private void chooseWhereTo(int blockX,int blockY) throws StrategoException
	{	
		mouseState.state = mouseState_.ChooseSoldierClick;

		try {
			engine.move(mouseState.chosedX, mouseState.chosedY, blockX, blockY);
			
			
		} catch (MoveException e) {
			e.printStackTrace();
		}
		
		currentPlayer = -1;
		lastPlayer = -2;
		getCurrentPlayer();
		getBoard();
		drawAllToBuffer();
	}
	
	
	 public enum mouseState_ 
	 {
	    	ChooseSoldierClick,
	    	ChooseWhereTo;
	 }
	 
	private class MouseState
	{
		public int chosedX = 0;
		public int chosedY = 0;
		public Rank chosedRank;
		public mouseState_ state = mouseState_.ChooseSoldierClick; 
	}
	
	private abstract class TimeoutObject
	{
		int timeout = 0;
		int hold = 0;
		int key;
		abstract void action();
	}
	
	private class TimeoutSquare extends TimeoutObject
	{
		Square square;
		int whereX;
		int whereY;
		public TimeoutSquare(int x,int y,Square s,int timeout,int key)
		{
			this.whereX = x;
			this.whereY = y;
			this.square = s;
			this.timeout = timeout;
			this.key = key;
		}
		
		void action()
		{
			drawPiece(whereX,whereY,square,false);
		}
	}
}